#include "file_server.h"
#include "json-builder.h"

extern "C" {
#include "http_util.h"
#include "file_util.h"
}

FileServer::FileServer(FileServerOption& option)
{
//	if (!fs::is_directory(option.base_dirctory)) {
//		throw "base_dirctory is not a directory";
//	}
	if (option.exts.size() == 0) {
		throw "exts can't be null";
	}
	if (option.base_url.empty()) {
		throw "base_url can't be null";
	}
	_option = option.Create();
}

void FileServer::QueryServerFiles(evhttp_request* req)
{
    enum evhttp_cmd_type cmd_type = evhttp_request_get_command(req);
    if (cmd_type != EVHTTP_REQ_GET) {
        cout << "request type is not Get" << endl;
        return_http_error(req, "request type is not Get");
        return;
    }

    const char* uri = evhttp_request_get_uri(req);
    struct evhttp_uri* evuri = evhttp_uri_parse(uri);
    const char* query = evhttp_uri_get_query(evuri);

    struct evkeyvalq params;
    evhttp_parse_query_str(query, &params);
    const char* start_str = evhttp_find_header(&params, "start");
    const char* limit_str = evhttp_find_header(&params, "limit");
    const char* path_str = evhttp_find_header(&params, "path");

    unsigned int start = 0;
    unsigned int limit = 20;
    fs::path dir_path = _option->base_dirctory;

    if (start_str) {
        start = atoi(start_str);
    }
    if (limit_str && atoi(limit_str) > 0) {
        limit = atoi(limit_str);
    }
    if (_option->enable_directory_search && path_str) {
        dir_path = dir_path / path_str;
    }

    cout << "start is " << start << endl;
    cout << "limit is " << limit << endl;
    cout << "search dir path is " << dir_path << endl;

    vector<FileInfo> fileinfos;
    SearchFileByExts(dir_path, start, limit, _option->exts, fileinfos);

    struct evbuffer* retbuff = evbuffer_new();
    struct evkeyvalq* reqheader = evhttp_request_get_output_headers(req);
    evhttp_add_header(reqheader, "Content-Type", "application/json");
    evhttp_add_header(reqheader, "charset", "UTF-8");

    json_value* top_jv = json_object_new(0);
    json_object_push(top_jv, "status", json_integer_new(200));

    json_value* data_arr_jv = json_array_new(fileinfos.size());
    for (size_t i = 0; i < fileinfos.size(); i++)
    {
        json_value* data_obj_jv = json_object_new(0);
        json_object_push(data_obj_jv, "file_name", json_string_new(fileinfos[i].file_name.c_str()));
        json_object_push(data_obj_jv, "ext", json_string_new(fileinfos[i].extension.c_str()));

        string file_url;
        if (path_str) {
            file_url = _option->base_url + "/" + path_str + "/" + fileinfos[i].file_name;
        }
        else {
            file_url = _option->base_url + "/" + fileinfos[i].file_name;
        }
        json_object_push(data_obj_jv, "url", json_string_new(file_url.c_str()));

        json_object_push(data_obj_jv, "last_write_time", json_integer_new(fileinfos[i].last_write_time));

        json_object_push(data_obj_jv,"file_size",json_integer_new(fileinfos[i].file_size));

        json_array_push(data_arr_jv, data_obj_jv);
    }
    json_object_push(top_jv, "data", data_arr_jv);

    json_object_push(top_jv, "start", json_integer_new(start));
    json_object_push(top_jv, "length", json_integer_new(fileinfos.size()));

    size_t json_strlen = json_measure(top_jv);
    char json_str[json_strlen];
    json_serialize(json_str, top_jv);

    json_value_free(top_jv);

#ifdef _WIN32
    size_t utf8_str_len = 100000;
    char utf8_str[utf8_str_len];
    gbk_to_utf8(json_str, utf8_str, utf8_str_len);

    evbuffer_add_printf(retbuff, utf8_str);
    evhttp_send_reply(req, HTTP_OK, "OK", retbuff);
    evbuffer_free(retbuff);
#else
    evbuffer_add_printf(retbuff, json_str);
    evhttp_send_reply(req, HTTP_OK, "OK", retbuff);
    evbuffer_free(retbuff);
#endif
}

void FileServer::QueryServerDirs(evhttp_request* req)
{
    enum evhttp_cmd_type cmd_type = evhttp_request_get_command(req);
    if (cmd_type != EVHTTP_REQ_GET) {
        cout << "request type is not Get" << endl;
        return_http_error(req, "request type is not Get");
        return;
    }

    const char* uri = evhttp_request_get_uri(req);
    struct evhttp_uri* evuri = evhttp_uri_parse(uri);
    const char* query = evhttp_uri_get_query(evuri);

    struct evkeyvalq params;
    evhttp_parse_query_str(query, &params);
    const char* start_str = evhttp_find_header(&params, "start");
    const char* limit_str = evhttp_find_header(&params, "limit");
    const char* path_str = evhttp_find_header(&params, "path");

    unsigned int start = 0;
    unsigned int limit = 20;
    fs::path dir_path = _option->base_dirctory;

    if (start_str) {
        start = atoi(start_str);
    }
    if (limit_str && atoi(limit_str) > 0) {
        limit = atoi(limit_str);
    }
    if (_option->enable_directory_search && path_str) {
        dir_path = dir_path / path_str;
    }

    vector<DirInfo> dirinfos;
    SearchDirs(dir_path, start, limit, dirinfos);

    struct evbuffer* retbuff = evbuffer_new();
    struct evkeyvalq* reqheader = evhttp_request_get_output_headers(req);
    evhttp_add_header(reqheader, "Content-Type", "application/json");
    evhttp_add_header(reqheader, "charset", "UTF-8");

    json_value* top_jv = json_object_new(0);
    json_object_push(top_jv, "status", json_integer_new(200));

    json_value* data_arr_jv = json_array_new(dirinfos.size());
    for (size_t i = 0; i < dirinfos.size(); i++)
    {
        json_value* data_obj_jv = json_object_new(0);
        json_object_push(data_obj_jv, "dir_name", json_string_new(dirinfos[i].dir_name.c_str()));
        json_object_push(data_obj_jv, "last_write_time", json_integer_new(dirinfos[i].last_write_time));

        json_array_push(data_arr_jv, data_obj_jv);
    }
    json_object_push(top_jv, "data", data_arr_jv);

    json_object_push(top_jv, "start", json_integer_new(start));
    json_object_push(top_jv, "length", json_integer_new(dirinfos.size()));

    size_t json_strlen = json_measure(top_jv);
    char json_str[json_strlen];
    json_serialize(json_str, top_jv);

    json_value_free(top_jv);

#ifdef _WIN32
    size_t utf8_str_len = 100000;
    char utf8_str[utf8_str_len];
    gbk_to_utf8(json_str, utf8_str, utf8_str_len);

    evbuffer_add_printf(retbuff, utf8_str);
    evhttp_send_reply(req, HTTP_OK, "OK", retbuff);
    evbuffer_free(retbuff);
#else
    evbuffer_add_printf(retbuff, json_str);
    evhttp_send_reply(req, HTTP_OK, "OK", retbuff);
    evbuffer_free(retbuff);
#endif
}

void FileServer::RenameServerFile(evhttp_request* req)
{
    enum evhttp_cmd_type cmd_type = evhttp_request_get_command(req);
    if (cmd_type != EVHTTP_REQ_POST) {
        cout << "request type is not Post" << endl;
        return_http_error(req, "request type is not Post");
        return;
    }

    const char* uri = evhttp_request_get_uri(req);
    struct evhttp_uri* evuri = evhttp_uri_parse(uri);
    const char* query = evhttp_uri_get_query(evuri);

    struct evkeyvalq params;
    evhttp_parse_query_str(query, &params);
    const char* filename = evhttp_find_header(&params, "filename");
    const char* new_filename = evhttp_find_header(&params, "new_filename");

    if (filename == 0 || new_filename == 0) {
        cout << "parameter error" << endl;
        return_http_error(req, "parameter error");
    }

    fs::path file_path = fs::path(_option->base_dirctory) / filename;
    fs::path new_file_path = fs::path(_option->base_dirctory) / new_filename;

    if (!fs::exists(file_path)) {
        cout << "source file is not exist" << endl;
        return_http_error(req, "source file is not exist");
        return;
    }
    if (fs::exists(new_file_path)) {
        cout << "target file is exist" << endl;
        return_http_error(req, "target file is exist");
        return;
    }

    fs::rename(file_path, new_file_path);

    struct evbuffer* retbuff = evbuffer_new();
    struct evkeyvalq* reqheader = evhttp_request_get_output_headers(req);
    evhttp_add_header(reqheader, "Content-Type", "application/json");
    evhttp_add_header(reqheader, "charset", "UTF-8");

    json_value* top_jv = json_object_new(0);
    json_object_push(top_jv, "status", json_integer_new(200));

    size_t json_strlen = json_measure(top_jv);
    char json_str[json_strlen];
    json_serialize(json_str, top_jv);

    json_value_free(top_jv);
#ifdef _WIN32
    size_t utf8_str_len = 100000;
    char utf8_str[utf8_str_len];
    gbk_to_utf8(json_str, utf8_str, utf8_str_len);

    evbuffer_add_printf(retbuff, utf8_str);
    evhttp_send_reply(req, HTTP_OK, "OK", retbuff);
    evbuffer_free(retbuff);
#else
    evbuffer_add_printf(retbuff, json_str);
    evhttp_send_reply(req, HTTP_OK, "OK", retbuff);
    evbuffer_free(retbuff);
#endif
}

void FileServer::DeleteServerFile(evhttp_request* req)
{
    enum evhttp_cmd_type cmd_type = evhttp_request_get_command(req);
    if (cmd_type != EVHTTP_REQ_POST) {
        cout << "request type is not Post" << endl;
        return_http_error(req, "request type is not Post");
        return;
    }

    const char* uri = evhttp_request_get_uri(req);
    struct evhttp_uri* evuri = evhttp_uri_parse(uri);
    const char* query = evhttp_uri_get_query(evuri);

    struct evkeyvalq params;
    evhttp_parse_query_str(query, &params);
    const char* filename = evhttp_find_header(&params, "filename");

    fs::path file_path = fs::path(_option->base_dirctory) / filename;
    if (!fs::exists(file_path)) {
        cout << "source file is not exist" << endl;
        return_http_error(req, "source file is not exist");
        return;
    }

    if (!fs::remove(file_path)) {
        cout << "file delete failed" << endl;
    }

    struct evbuffer* retbuff = evbuffer_new();
    struct evkeyvalq* reqheader = evhttp_request_get_output_headers(req);
    evhttp_add_header(reqheader, "Content-Type", "application/json");
    evhttp_add_header(reqheader, "charset", "UTF-8");

    json_value* top_jv = json_object_new(0);
    json_object_push(top_jv, "status", json_integer_new(200));

    size_t json_strlen = json_measure(top_jv);
    char json_str[json_strlen];
    json_serialize(json_str, top_jv);

    json_value_free(top_jv);

#ifdef _WIN32
    size_t utf8_str_len = 100000;
    char utf8_str[utf8_str_len];
    gbk_to_utf8(json_str, utf8_str, utf8_str_len);

    evbuffer_add_printf(retbuff, utf8_str);
    evhttp_send_reply(req, HTTP_OK, "OK", retbuff);
    evbuffer_free(retbuff);
#else
    evbuffer_add_printf(retbuff, json_str);
    evhttp_send_reply(req, HTTP_OK, "OK", retbuff);
    evbuffer_free(retbuff);
#endif
}

int FileServer::SearchFileByExts(fs::path dir_path, unsigned int start, unsigned int limit, vector<string> exts, vector<FileInfo>& file_infos)
{
    if (!fs::is_directory(dir_path)) {
        return -1;
    };
    file_infos.clear();

    vector<fs::directory_entry> dentrys;
    for (auto& iter: fs::directory_iterator(dir_path)){
         if (iter.is_regular_file()){
            string extension = iter.path().extension().string();
            bool extension_ok = false;
            for (auto& ext:exts)
            {
                if (!ext.empty() && ext == extension) {
                    extension_ok = true;
                    break;
                }
            }
            if (extension_ok){
                dentrys.emplace_back(iter);
            }
        }
    }

    sort(dentrys.begin(),dentrys.end(),[](fs::directory_entry a,fs::directory_entry b){
        return to_time_t(a.last_write_time()) > to_time_t(b.last_write_time());
    });

    unsigned int file_index = 0;
    while (file_infos.size() < limit && file_index < dentrys.size())
    {
        if (file_index >= start && file_index < start + limit){
            FileInfo fi;
            fi.extension = dentrys[file_index].path().extension().string();
            fi.file_full_path = dentrys[file_index].path().string();
            fi.file_name = dentrys[file_index].path().filename().string();
            fi.last_write_time = to_time_t(dentrys[file_index].last_write_time());
            fi.file_size = dentrys[file_index].file_size();
            file_infos.emplace_back(fi);
        }
        file_index++;
    }
    
    // for (auto& iter: fs::directory_iterator(dir_path))
    // {
    //     if (file_infos.size() >= limit) {
    //         break;
    //     }

    //     if (iter.is_regular_file()) {
    //         string extension = iter.path().extension().string();
    //         bool extension_ok = false;
    //         for (auto& ext:exts)
    //         {
    //             if (!ext.empty() && ext == extension) {
    //                 extension_ok = true;
    //                 break;
    //             }
    //         }
    //         if (extension_ok) {

    //             if (file_index >= start && file_index < start + limit) {
    //                 FileInfo fi;
    //                 fi.extension = iter.path().extension().string();
    //                 fi.file_full_path = iter.path().string();
    //                 fi.file_name = iter.path().filename().string();
    //                 fi.last_write_time = to_time_t(iter.last_write_time());
    //                 file_infos.emplace_back(fi);
    //             }
    //             file_index++;
    //         }
    //     }
    // }
    return 0;
}

int FileServer::SearchDirs(fs::path dir_path, unsigned int start, unsigned int limit, vector<DirInfo>& dir_infos)
{
    if (!fs::is_directory(dir_path)) {
        return -1;
    };
    dir_infos.clear();

    vector<fs::directory_entry> dentrys;
    for (auto& iter : fs::directory_iterator(dir_path)) {
        if (iter.is_directory()){
            dentrys.emplace_back(iter);
        }
    }
    sort(dentrys.begin(),dentrys.end(),[](fs::directory_entry a,fs::directory_entry b){
        return to_time_t(a.last_write_time()) > to_time_t(b.last_write_time());
    });

    unsigned int dir_index = 0;
    while (dir_infos.size() < limit && dir_index < dentrys.size())
    {
        if (dir_index >= start && dir_index < start + limit) {
            DirInfo di;
            di.dir_full_path = dentrys[dir_index].path().string();
            di.dir_name = dentrys[dir_index].path().filename().string();
            di.last_write_time = to_time_t(dentrys[dir_index].last_write_time());
            dir_infos.emplace_back(di);
        }
        dir_index++;
    }
    

    // for (auto& iter : fs::directory_iterator(dir_path)) {
    //     if (dir_infos.size() >= limit) {
    //         break;
    //     }
    //     if (iter.is_directory()) {
    //         if (dir_index >= start && dir_index < start + limit) {
    //             DirInfo di;
    //             di.dir_full_path = iter.path().string();
    //             di.dir_name = iter.path().filename().string();
    //             dir_infos.emplace_back(di);
    //         }
    //         dir_index++;
    //     }
    // }
    return 0;
}
